import tkinter as tk
import os
from log_manager import LogManager
import hashlib
import struct
import time
import threading

from serial_disk_interface import SerialDiskInterface
from ui_manager import UIManager
from protocol_parser import ProtocolParser

class SerialDiskGUI(SerialDiskInterface):
    def __init__(self, config_path):
        super().__init__(config_path)
        self.root = tk.Tk()
        self.ui_manager = UIManager(self.root)

    def start(self):
        """启动GUI应用"""
        self.ui_manager.create_main_window()
        self._init_bindings()
        self._load_initial_state()
        self.root.mainloop()
        
    def stop(self):
        """停止GUI应用"""
        try:
            if self.device_manager and self.device_manager.is_connected():
                # 如果正在传输，则取消传输
                if (hasattr(self.device_manager, 'transfer_manager') and 
                    self.device_manager.transfer_manager.is_transferring()):
                    self.device_manager.transfer_manager.cancel_transfer(silent=True)
                # 断开连接
                try:
                    self.device_manager.disconnect()
                except:
                    pass
        finally:
            # 保存配置并退出
            self.config_manager.save()
            self.root.quit()
            
    def handle_device_error(self, error):
        """处理设备错误"""
        self.ui_manager.show_error(f"设备错误: {error}")
        
    def _init_bindings(self):
        """初始化事件绑定"""
        # 连接按钮事件
        self.ui_manager.bind_button('connect', self._handle_connect)
        # 刷新按钮事件
        self.ui_manager.bind_button('refresh', self._scan_ports)
        
        # 设备信息按钮事件
        self.ui_manager.bind_button('device_info', self._show_device_info)
        
        # U盘信息按钮事件 
        self.ui_manager.bind_button('disk_info', self._show_disk_info)
        
        # 取消传输按钮事件
        self.ui_manager.bind_button('cancel', self._cancel_transfer)
        
        # 文件列表双击事件
        self.ui_manager.bind_list('local', '<Double-1>', self._handle_local_double_click)
        self.ui_manager.bind_list('device', '<Double-1>', self._handle_device_double_click)
        # 右键菜单事件
        self.ui_manager.bind_list('local', '<Button-3>', self._show_local_menu)
        self.ui_manager.bind_list('device', '<Button-3>', self._show_device_menu)
        
        # 路径变更事件
        self.ui_manager.bind_path_change('local', lambda path: self._update_local_file_list(path))
        self.ui_manager.bind_path_change('device', self._handle_device_path_change)
        
    def _load_initial_state(self):
        """加载初始状态"""
        # 加载本地文件列表
        local_path = self.config_manager.get('local_path')
        if not local_path:
            # 如果没有配置本地路径，使用当前目录
            local_path = os.path.abspath('.')
            self.config_manager.set('local_path', local_path)
        
        if os.path.exists(local_path):
            self._update_local_file_list(local_path)
        else:
            LogManager.warning(f"本地路径不存在: {local_path}")
        
        # 扫描串口
        self._scan_ports()
        
    def _handle_connect(self):
        """处理连接按钮事件"""
        if not self.device_manager.is_connected():
            port = self.ui_manager.get_port()
            if self.device_manager.connect(port):
                self._init_device_manager()
                self.ui_manager.set_connected_state(True)
                # 设置初始路径为/usb
                self.config_manager.set('device_path', '/usb')
                self._update_device_files('/usb')
            else:
                self.ui_manager.show_error("连接失败")
        else:
            self.device_manager.disconnect()
            self.ui_manager.set_connected_state(False)
            
    def _handle_local_double_click(self, event):
        """处理本地文件列表双击事件"""
        item = self.ui_manager.get_selected_item('local')
        if not item:
            return
            
        path = os.path.join(self.config_manager.get('local_path'), item['name'])
        if os.path.isdir(path):
            self._update_local_file_list(path)
        else:
            self._handle_transfer(item, is_upload=True)
            
    def _handle_device_double_click(self, event):
        """处理设备文件列表双击事件"""
        item = self.ui_manager.get_selected_item('device')
        if not item:
            return
            
        if item['is_directory']:
            new_path = os.path.join(self.config_manager.get('device_path'), 
                                  item['name']).replace('\\', '/')
            self._update_device_files(new_path)
        else:
            self._handle_transfer(item, is_upload=False)

    def _handle_transfer(self, source_item, is_upload):
        """处理文件传输
        
        Args:
            source_item (dict): 源文件/文件夹信息字典
            is_upload (bool): 是否为上传操作
        """
        try:
            if not self.device_manager.is_connected():
                self.ui_manager.show_error("设备未连接")
                return

            # 准备路径信息
            if is_upload:
                local_path = os.path.join(
                    self.config_manager.get('local_path'),
                    source_item['name']
                )
                remote_dir = self.config_manager.get('device_path', '/')
                remote_path = os.path.join(remote_dir, source_item['name']).replace('\\', '/')
                
                is_dir = os.path.isdir(local_path)
                size = 0 if is_dir else os.path.getsize(local_path)
                LogManager.info(f"传输文件: {local_path} -> {remote_path}, 大小: {size}，是否文件夹: {is_dir}")   
            else:
                remote_path = os.path.join(
                    self.config_manager.get('device_path'),
                    source_item['name']
                ).replace('\\', '/')
                local_dir = self.config_manager.get('local_path')
                local_path = os.path.join(local_dir, source_item['name'])
                
                is_dir = source_item['is_directory']
                size = 0 if is_dir else source_item['size']
                LogManager.info(f"传输文件: {remote_path} -> {local_path}, 大小: {size}，是否文件夹: {is_dir}")

            # 显示进度条
            self.ui_manager.show_progress_bar()
            operation = "上传" if is_upload else "下载"
            name = os.path.basename(local_path)
            self.ui_manager.add_log(f"开始{operation}: {name}" + 
                                  (f" ({size})" if not is_dir else ""))

            # 开始传输
            if is_upload:
                self.device_manager.transfer_manager.transfer_to_device(local_path, remote_path, is_dir , size)
            else:
                self.device_manager.transfer_manager.transfer_from_device(remote_path, local_path, is_dir , size)

        except Exception as e:
            LogManager.error(f"传输失败 178: {str(e)}")
            self.ui_manager.show_error(f"传输失败: {str(e)}")
            self.ui_manager.hide_progress_bar()

    def _show_local_menu(self, event):
        """显示本地文件右键菜单"""
        # 获取选中的项目
        selection = self.ui_manager.get_selected_item('local')
        if not selection:
            return
            
        # 创建菜单
        menu = tk.Menu(self.root, tearoff=0)
        
        # 添加菜单项
        menu.add_command(
            label="上传到设备",
            command=lambda: self._on_upload(selection)
        )
        
        # 显示菜单
        try:
            menu.tk_popup(event.x_root, event.y_root)
        finally:
            menu.grab_release()
            
    def _show_device_menu(self, event):
        """显示设备文件右键菜单"""
        # 获取选中的项目
        selection = self.ui_manager.get_selected_item('device')
        if not selection:
            return
            
        # 创建菜单
        menu = tk.Menu(self.root, tearoff=0)
        
        # 添加菜单项
        menu.add_command(
            label="下载到本地",
            command=lambda: self._on_download(selection)
        )
        menu.add_command(
            label="刷新",
            command=lambda: self._on_refresh(is_local=False)
        )
        menu.add_separator()
        menu.add_command(
            label="新建文件夹",
            command=self._on_mkdir
        )
        
        # 显示菜单
        try:
            menu.tk_popup(event.x_root, event.y_root)
        finally:
            menu.grab_release()
            

    def _update_local_file_list(self, path):
        """更新本地文件列表"""
        try:
            if not os.path.exists(path):
                self.ui_manager.show_error(f"路径不存在: {path}")
                return
                
            # 更新配置
            self.config_manager.set('local_path', path)
            
            # 更新路径显示
            self.ui_manager.set_path('local', path)
            
            # 获取文件列表
            files = []
            for item in os.listdir(path):
                item_path = os.path.join(path, item)
                try:
                    stat = os.stat(item_path)
                    is_dir = os.path.isdir(item_path)
                    files.append({
                        'name': item,
                        'type': 1 if is_dir else 0,  # 保持与设备端一致
                        'size': 0 if is_dir else stat.st_size,
                        'mtime': int(stat.st_mtime),
                        'is_directory': is_dir  # 添加is_directory字段
                    })
                except OSError:
                    continue
                
            # 更新列表显示
            self.ui_manager.update_file_list(True, files)
            
        except Exception as e:
            self.ui_manager.show_error(f"更新本地文件列表失败: {str(e)}")
            
    def _scan_ports(self):
        """扫描并更新可用串口列表"""
        try:
            # 获取可用串口列表
            ports = self.device_manager.get_available_ports()
            
            # 更新UI中的串口选择下拉框
            self.ui_manager.update_port_list(ports)
            
            # 如果有保存的上次使用的端口，则选择它
            last_port = self.config_manager.get('last_port')
            if last_port and last_port in ports:
                self.ui_manager.set_port(last_port)
                
        except Exception as e:
            self.ui_manager.show_error(f"扫描串口失败: {str(e)}") 

    def _update_device_files(self, path=None):
        """更新设备文件列表
        
        Args:
            path (str, optional): 要显示的设备文件夹路径。如果为None，则使用配置中的路径
        """
        try:
            # 如果没有指定路径，使用配置中的设备路径
            if path is None:
                path = self.config_manager.get('device_path', '/')
            else:
                self.config_manager.set('device_path', path)
            
            # 获取设备文件列表
            files = self.device_manager.list_device_files(path)
            if files is None:
                raise Exception("获取文件列表失败")
            
            # 更新UI显示
            self.ui_manager.update_file_list(False, files)  # False表示设备列表
            self.ui_manager.set_path('device', path)
            
        except Exception as e:
            self.ui_manager.show_error(f"更新设备文件列表失败: {str(e)}")

    def _on_upload(self, selection):
        """处理上传文件到设备的操作
        
        Args:
            selection: 选中的文件或文件夹名称
        """
        if selection:
            self._handle_transfer(selection, is_upload=True)

    def _on_download(self, selection):
        """处理从设备下载文件的操作
        
        Args:
            selection: 选中的文件名称
        """
        if selection:
            self._handle_transfer(selection, is_upload=False)

    def _on_refresh(self, is_local=True):
        """刷新文件列表
        
        Args:
            is_local (bool): True表示刷新本地列表，False表示刷新设备列表
        """
        try:
            if is_local:
                local_path = self.config_manager.get('local_path')
                if os.path.exists(local_path):
                    self._update_local_file_list(local_path)
            else:
                if not self.device_manager.is_connected():
                    self.ui_manager.show_error("设备未连接")
                    return
                self._update_device_files()
            
        except Exception as e:
            self.ui_manager.show_error(f"刷新失败: {str(e)}")

    def _on_mkdir(self):
        """在设备上创建新文件夹"""
        try:
            # 检查设备连接状态
            if not self.device_manager.is_connected():
                self.ui_manager.show_error("设备未连接")
                return
            
            # 弹出输入对话框
            folder_name = self.ui_manager.show_input_dialog(
                "新建文件夹",
                "请输入文件夹名称："
            )
            
            if not folder_name:  # 用户取消或未输入
                return
            
            # 构建完整路径
            current_path = self.config_manager.get('device_path', '/')
            new_folder_path = os.path.join(current_path, folder_name).replace('\\', '/')
            
            # 创建文件夹
            if not self.device_manager.create_directory(new_folder_path):
                raise Exception("创建文件夹失败")
            
            # 刷新文件列表
            self._update_device_files()
            self.ui_manager.add_log(f"已创建文件夹: {folder_name}")
            
        except Exception as e:
            self.ui_manager.show_error(f"创建文件夹失败: {str(e)}") 

    def _handle_device_path_change(self, path):
        """处理设备路径变更事件"""
        LogManager.debug(f"设备路径变更: {path}")
        self._update_device_files(path) 


    def handle_transfer_progress(self, transferred, total, status):
        """处理传输进度回调
        
        Args:
            transferred: 已传输字节数
            total: 总字节数
            status: 状态信息（包含速度和进度）
        """
        if total > 0:
            # 在主线程中更新UI
            self.root.after(0, self.ui_manager.update_progress, 
                          (transferred / total) * 100, 
                          status)

    def handle_transfer_complete(self, success, error, md5=None):
        """处理传输完成回调
        
        Args:
            success: 是否成功
            error: 错误信息（如果失败）
            md5: 文件的MD5值（如果成功）
        """
        LogManager.info(f"传输完成: {success}, 错误: {error}, MD5: {md5}")
        def _update_ui():
            if success:
                if md5:
                    self.ui_manager.add_log(f"传输完成，文件MD5: {md5}")
                else:
                    self.ui_manager.add_log("传输完成")
                self.ui_manager.hide_progress_bar()
                # 如果是上传，刷新设备文件列表
                if hasattr(self, 'device_manager') and self.device_manager.is_connected():
                    self._update_device_files()
            else:
                self.ui_manager.show_error(f"传输失败: {error}")
                self.ui_manager.hide_progress_bar()

        # 在主线程中更新UI
        self.root.after(0, _update_ui)

    def _show_device_info(self):
        """显示设备信息"""
        try:
            if not self.device_manager.is_connected():
                self.ui_manager.show_error("请先连接设备")
                return
            
            # 获取设备信息
            device_info = self.device_manager.get_device_info()
            if not device_info:
                self.ui_manager.show_error("获取设备信息失败")
                return
            
            # 显示设备信息
            info_text = (
                f"硬件版本: {device_info.get('hw_version', '未知')}\n"
                f"软件版本: {device_info.get('sw_version', '未知')}\n"
                f"协议版本: {device_info.get('proto_version', '未知')}"
            )
            self.ui_manager.add_log("设备信息：")
            for line in info_text.split('\n'):
                self.ui_manager.add_log(line)
            
        except Exception as e:
            LogManager.error(f"获取设备信息失败: {str(e)}")
            self.ui_manager.show_error(f"获取设备信息失败: {str(e)}")

    def _show_disk_info(self):
        """显示U盘信息"""
        try:
            if not self.device_manager.is_connected():
                self.ui_manager.show_error("请先连接设备")
                return
            
            # 获取U盘信息
            disk_info = self.device_manager.get_disk_info()
            if not disk_info:
                self.ui_manager.show_error("获取U盘信息失败")
                return
            
            # 显示U盘信息
            info_text = (
                f"扇区数: {disk_info['sector_count']}\n"
                f"扇区大小: {disk_info['sector_size']}字节\n"
                f"产品ID: 0x{disk_info['idProduct']:04X}\n"
                f"厂商ID: 0x{disk_info['idVendor']:04X}\n"
                f"制造商: {disk_info['iManufacturer']}\n"
                f"产品: {disk_info['iProduct']}\n"
                f"序列号: {disk_info['iSerialNumber']}"
                f"容量: {disk_info['capacity']/1024:.1f}GB\n" if disk_info['capacity'] >= 1024 else f"容量: {disk_info['capacity']:.1f}MB\n"
            )
            self.ui_manager.add_log("U盘信息：")
            for line in info_text.split('\n'):
                self.ui_manager.add_log(line)
            
        except Exception as e:
            LogManager.error(f"获取U盘信息失败: {str(e)}")
            self.ui_manager.show_error(f"获取U盘信息失败: {str(e)}")

    def _cancel_transfer(self):
        """取消传输"""
        try:
            if not hasattr(self.device_manager, 'transfer_manager'):
                return
            LogManager.debug("检查是否正在传输 is_transferring: %s", self.device_manager.transfer_manager.is_transferring())
            if not self.device_manager.transfer_manager.is_transferring():
                return
            
            # 执行取消操作
            self.device_manager.transfer_manager.cancel_transfer()
            self.ui_manager.hide_progress_bar()
            self.ui_manager.add_log("用户取消了传输")
            LogManager.info("用户取消了传输")
            
        except Exception as e:
            LogManager.error(f"取消传输失败: {str(e)}")
            self.ui_manager.show_error(f"取消传输失败: {str(e)}")